package cz.vse.po1715.lode.net.server;

import cz.vse.po1715.lode.hra.Hra;
import cz.vse.po1715.lode.hra.Lode;
import cz.vse.po1715.lode.net.obecne.SpravcePrikazu;
import cz.vse.po1715.lode.net.obecne.HerniUdalost;
import cz.vse.po1715.lode.net.obecne.SHrac;
import cz.vse.po1715.lode.net.obecne.IHerniUdalost;
import cz.vse.po1715.lode.net.obecne.ISHrac;
import cz.vse.po1715.lode.net.obecne.KonfiguraceHry;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.StringTokenizer;


/*******************************************************************************
 * Instance třídy {@code SprávceHry} představují správce
 * jednotlivých instancí hry Lodě běžící na serveru
 *
 * @author    Dominik KOSTKA
 * @version   0.00.000
 */
public class SprávceHry extends ASprávceHry
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================
//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
//== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================
//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================
    /** seznam spravovaných hráčů připojených k serveru */
    protected Hashtable hraci;

    /** seznam spravovaných her */
    protected HashMap hry;

    /** aktuálně zpracovávaný tah */
//    int tah[][];

    /** text strings for results */
    String vysledky[] = { "Vyhral jste.", "Prohral jste."};
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     *
     */
    public SprávceHry()
    {
    }



//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================
//== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================

    @Override
    public String getNazevHry() {
        return "Lode";
    }

    @Override
    protected void initSpravceHry(KonfiguraceHry konfigurace) {
        log.info("inicializace spravce hry");
	hraci = new Hashtable();
	hry = new HashMap();
    }

    @Override
    public ISHrac vytvorHrac() {
        SHrac hrac = new SHrac();
	hrac.setIdRelace(server.dalsiIDRelace());
	return hrac;
    }

    /**
     * 
     */
    @Override
    public IHerniUdalost vytvorHerniUdalost() {
	return new HerniUdalost();
    }

    @Override
    protected void zpracujUdalost(IHerniUdalost událost) {
        switch (událost.getTyp()) {
	case HerniUdalost.K_PŘIPOJENÍ:
	    pripoj(událost);
	    break;
	case HerniUdalost.K_ODPOJENÍ:
	    odpoj(událost);
	    break;
	case HerniUdalost.K_PŘIHLÁŠENÍ_HRA:
	    vytvorHru(událost);
	    break;
	case HerniUdalost.K_ODHLÁSIT_HRA:
	    ukonciHru(událost);
	    break;
	case HerniUdalost.K_CHAT_ZPRÁVA:
	    chatuj(událost);
	    break;
	case HerniUdalost.K_UDER:
	    provedUder(událost);
	    break;
        case HerniUdalost.K_UMISTI_LOD:
	    umistiLod(událost);
	    break;
	case HerniUdalost.K_GET_HRÁČI:
	    getHraci(událost);
	    break;
        }
    }

    /**
     * spravuje připojovací události
     */
    protected void pripoj(IHerniUdalost udalost) {
	String idHrac = udalost.getIdHrac();

	SHrac hrac = (SHrac) Server.getHracPodleId(idHrac);

        if (hrac == null) {
	    log.info("Pokus o prihlaseni neznameho hrace.");
	    return;
	}

	if (hrac.přihlášen())
	    log.warning("Prihlaseni jiz prihlaseneho hrace: " + idHrac);

	hrac.setPřihlášen(true);

	// posle ACK hraci
	HerniUdalost sp = new HerniUdalost(HerniUdalost.S_PŘIPOJENÍ_ACK_OK);
	posliUdalost(sp, hrac);

	// řekne všem ostatním hráčům na serveru, že se přihlásil nový hráč
	HerniUdalost shp = new HerniUdalost(HerniUdalost.SH_PŘIPOJENÍ, hrac.getIdHrac());
	posliUdalostHromadne(shp, hraci.values());

	// přidá hráče do seznamu hráčů na serveru
	hraci.put(idHrac, hrac);

	// vrátí aktualizovaný seznam hráčů na serveru
	getHraci(udalost);

	log.info("Prihlaseni, hrac: " + idHrac + ", hraci online: " + hraci.size());
    }

    /**
     * spravuje odpojovaci udalosti
     */
    protected void odpoj(IHerniUdalost udalost) {
	String idHrac = udalost.getIdHrac();
	SHrac hrac = (SHrac) hraci.get(idHrac);

	// pokud je hráč ve hře, nejprve jí ukončí
	if (hrac.veHre()) {
	    ukonciHru(udalost);
	}

	// vymaže hráče ze seznamu hráčů na serveru
	hraci.remove(idHrac);

	// pošle hráču oznámení o jeho odpojení od serveru
	HerniUdalost so = new HerniUdalost(HerniUdalost.S_ODPOJENÍ,
                "Byl jste odpojen od serveru.");
	posliUdalost(so, hrac);

	// pošle všem ostatním hráčům oznámení, že se zadaný hráč odpojil od
        // serveru
	HerniUdalost shOdp = new HerniUdalost(HerniUdalost.SH_ODPOJENÍ, hrac.getIdHrac());
	posliUdalostHromadne(shOdp, hraci.values());

	log.info("Odhlaseni, hrac: " + idHrac + ", hraci online: " + hraci.size());
    }

    /**
     * spustí novou hru se zadaným protihráčem a pošle oběma hráčům zprávu,
     * která je o tom informuje
     * metoda je typu synchronized, takže není problém, aby více
     * hráčů začalo novou hru se stejným protihráčem. Protihrac bude
     * bezprostredne po skonceni hry zarazen do dalsi, pokud je do ni prihlasen
     */
    protected synchronized void vytvorHru(IHerniUdalost událost) {
	String idHrac1 = událost.getIdHrac();
        String idHrac2 = událost.getZprava();
        
        SHrac hrac1 = (SHrac) hraci.get(idHrac1);

        if (idHrac2.equals(idHrac1)) {
	    HerniUdalost sph = new HerniUdalost(HerniUdalost.S_PŘIHLÁŠENÍ_HRA_ACK_FAIL);
	    sph.setZprava("Promiňte, ale nemůžete hrát sám proti sobě.");
	    posliUdalost(sph, hrac1);
	    return;
	}

        SHrac hrac2 = (SHrac) hraci.get(idHrac2);

	if (hrac2 == null) {
	    HerniUdalost sph = new HerniUdalost(HerniUdalost.S_PŘIHLÁŠENÍ_HRA_ACK_FAIL);
	    sph.setZprava("Tento hrac nemuze byt vasim protivnikem, protoze neni" +
                    "pripojen k serveru.");
	    posliUdalost(sph, hrac1);
	    return;
	}

        // kontrola, jestli neni zakladajici hrac uz v jine hre
        if (hrac1.veHre()) {
	    HerniUdalost sph = new HerniUdalost
                    (HerniUdalost.S_PŘIHLÁŠENÍ_HRA_ACK_FAIL);
	    sph.setZprava("Už jste v jine hre.");
	    posliUdalost(sph, hrac1);
	    return;
	}

        // kontrola, jestli neni zadany souper uz v jine hre
        if (hrac2.veHre()) {
	    HerniUdalost sph = new HerniUdalost
                    (HerniUdalost.S_PŘIHLÁŠENÍ_HRA_ACK_FAIL);
	    sph.setZprava("Zvoleny souper uz je v jine hre.");
	    posliUdalost(sph, hrac1);
	    return;
	}

	// vytvoří novou instanci hry a předá jí odkaz na instance
        // příslušných hráčů
	Hra hra = new Hra(hrac1, hrac2);
	hry.put("" + hra.idHra, hra);
	hrac1.setIdHra(hra.idHra);
        hrac2.setIdHra(hra.idHra);

	// pošle oběma hráčům zprávu o jejich připojení do nové hry
        HerniUdalost sph = new HerniUdalost(HerniUdalost.S_PŘIHLÁŠENÍ_HRA_ACK_OK);
	sph.setZprava("Pripojil jste se do hry: " + hrac1.getIdHrac() + " vs. " + hrac2.getIdHrac());
	posliUdalost(sph, hrac1);

	sph = new HerniUdalost(HerniUdalost.S_PŘIHLÁŠENÍ_HRA_ACK_OK);
	sph.setZprava("Pripojil jste se do hry: " + hrac1.getIdHrac() + " vs. " + hrac2.getIdHrac());
	posliUdalost(sph, hrac2);
    }

    /**
     * Zpracuje herni udalost predstavujici uder hrace na zvolene souradnice
     * hraciho pole hry
     * Pokud jeste neni hraci pole pripraveno, informuje hrace o nutnosti
     * doplnit lode do hraciho pole, jinak provede uder. Nasledne odesle udalost
     * informujici hrace o vysledku jeho pokusu
     */
    protected void provedUder(IHerniUdalost udalost){
        String idHrac = udalost.getIdHrac();
        SHrac hrac = (SHrac) hraci.get(idHrac);
        Hra hra = (Hra) hry.get("" + hrac.getIdHra());

        String zprava  = udalost.getZprava();
        String parametry[] = SpravcePrikazu.parametrizujString(zprava);

        boolean lodeUmisteny;
        
        if(hrac == hra.hrac1)   lodeUmisteny = Lode.vsechny_lode;
        else    lodeUmisteny = Lode.vsechny_lode2;
        HerniUdalost ku;
        String volba, vys;
        
        if(lodeUmisteny){
            ku = new HerniUdalost(HerniUdalost.K_UDER);
            volba = hrac.getIdHrac() + " uderil na pole: " + parametry[1] + "\n";


        }else{
            ku = new HerniUdalost(HerniUdalost.S_UDER_ACK_CHYBA);
            volba = "Nejsou jeste rozmisteny vsechny lode";
            
        }

        int h;
        
        if(hrac == hra.hrac1)   h = 1;
        else   h = 2;

        int y = Integer.parseInt(parametry[1].substring(0, 1));
        String znak = parametry[1].substring(1, 2);
        
        boolean vysledek = Hra.Strelba(h, znak, y);

        if(vysledek)ku.setZprava(volba + "Zásah!");
        else ku.setZprava(volba + "Vedle!");
        
        posliUdalost(ku, hra.hrac1);
        posliUdalost(ku, hra.hrac2);
    }

    /**
     * Zpracuje herni udalost predstavujici umisteni lode prislusnym hracem
     * na jeho verzi herniho pole
     */
    protected void umistiLod(IHerniUdalost udalost){
        String idHrac = udalost.getIdHrac();
	SHrac hrac = (SHrac) hraci.get(idHrac);
	Hra hra = (Hra) hry.get("" + hrac.getIdHra());

        String zprava  = udalost.getZprava();
        String parametry[] = SpravcePrikazu.parametrizujString(zprava);
        int y = Integer.parseInt(parametry[1].substring(0, 1));
        String znak = parametry[1].substring(1, 2);
//        if(y > 10){
//            y = Integer.parseInt((parametry2[1]);
//            znak = parametry2[0];
        
//
//        Lode.Plavidlo(parametry[1], znak, y, parametry[3]);

        HerniUdalost kul = new HerniUdalost(HerniUdalost.K_UMISTI_LOD);
//        posliUdalost(kul, hrac);
    }

    /**
     * Ukonci zadanou hru a posle vsem hracum zpravu, ze je ukoncena
     */
    protected void ukonciHru(IHerniUdalost udalost) {
	String idHrac = udalost.getIdHrac();
	SHrac hrac = (SHrac) hraci.get(idHrac);
	Hra hra = (Hra) hry.get("" + hrac.getIdHra());
//
	if (hra == null) {
	    HerniUdalost sph = new HerniUdalost(HerniUdalost.S_PŘIHLÁŠENÍ_HRA_ACK_FAIL);
	    sph.setZprava("Nemuzete ukoncit hru, protoze zadnou nehrajete");
	    posliUdalost(sph, hrac);
	    return;
	}

	SHrac hrac1 = hra.hrac1;
	SHrac hrac2 = hra.hrac2;
	hrac1.setVeHře(false);
	hrac2.setVeHře(false);
//	hráč1.setIdHra(g.getGameId());
//	hráč2.setIdHra(g.getGameId());
        String konec = "Konec hry, hrac " + hrac.getIdHrac() + " ukoncil hru.\n";
        String statistika = "";
	hry.remove("" + hra.idHra);
	HerniUdalost shho = new HerniUdalost(HerniUdalost.SH_HRÁČ_ODHLÁŠEN,
                konec + statistika);
	posliUdalost(shho, hrac1);
	shho = new HerniUdalost(HerniUdalost.SH_HRÁČ_ODHLÁŠEN, 
                konec + statistika);
	posliUdalost(shho, hrac2);
    }

    /**
     * posílá ostatním hráčum chatovací zprávu od jiného hráčů
     */
    protected void chatuj(IHerniUdalost událost) {
	událost.setTyp(HerniUdalost.SH_CHAT_ZPRÁVA);
	posliUdalostHromadne(událost, hraci.values());
	log.info("Chat, hrac " + událost.getIdHrac() + " pise: " + událost.getZprava());
    }

    /**
     * vrací seznam hráčů připojených k serveru
     */
    protected void getHraci(IHerniUdalost udalost) {
	String idHrac = udalost.getIdHrac();
	ISHrac hrac = (ISHrac) hraci.get(idHrac);

	StringBuffer sb = new StringBuffer();
	sb.append("Hraci online:\n");
	Iterator i = hraci.values().iterator();
	while(i.hasNext()) {
	    SHrac h = (SHrac) i.next();
	    sb.append(h.getIdHrac());
	    sb.append("\n");
	}

	HerniUdalost sgh = new HerniUdalost(HerniUdalost.S_GET_HRÁČI, sb.toString());
	posliUdalost(sgh, hrac);
    }
    

//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
//== INTERNÍ DATOVÉ TYPY =======================================================
//== TESTOVACÍ METODY A TŘÍDY ==================================================
//
//    /***************************************************************************
//     * Testovací metoda.
//     */
//    public static void test()
//    {
//        SprávceHry inst = new SprávceHry();
//    }
//    /** @param args Parametry příkazového řádku - nepoužívané. */
//    public static void main( String[] args )  {  test();  }
}
